package com.uiot.gen.core;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.jfinal.kit.Kv;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.Prop;
import com.jfinal.kit.PropKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import com.uiot.gen.model.ColumnEntity;
import com.uiot.gen.model.TableEntity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 代码生成器
 * @author
 */
public class JfGenerator {

    Log log = LogFactory.get();
    
    public static final JfGenerator me  = new JfGenerator();
    protected final JfEnjoy jfEngine    = new JfEnjoy();

    Prop p = PropKit.use("generator.txt");

    protected Kv tablemetaMap = null;

    protected String peojectName = p.get("peojectName");
    protected String author = p.get("author");
    protected String packageBase = p.get("packageBase");
    protected String modelUrl = p.get("modelUrl");
    protected String formUrl = p.get("formUrl");
    protected String queryUrl = p.get("queryUrl");
    protected String mapperUrl = p.get("mapperUrl");
    protected String controllerUrl = p.get("controllerUrl");
    protected String feginApiFallbackUrl = p.get("feginApiFallbackUrl");
    protected String feginApiUrl = p.get("feginApiUrl");
    protected String serviceUrl = p.get("serviceUrl");
    protected String serviceImplUrl = p.get("serviceImplUrl");
    protected String prefixes = p.get("prefixes");

    protected Boolean hasBigDecimal = false;

    /** 类型转换 */
    public static Map<String, String> javaTypeMap = new HashMap<String, String>();

    static {
        javaTypeMap.put("tinyint", "Integer");
        javaTypeMap.put("smallint", "Integer");
        javaTypeMap.put("mediumint", "Integer");
        javaTypeMap.put("int", "Integer");
        javaTypeMap.put("number", "Integer");
        javaTypeMap.put("integer", "integer");
        javaTypeMap.put("bigint", "Long");
        javaTypeMap.put("float", "Float");
        javaTypeMap.put("double", "Double");
        javaTypeMap.put("decimal", "BigDecimal");
        javaTypeMap.put("bit", "Boolean");
        javaTypeMap.put("char", "String");
        javaTypeMap.put("varchar", "String");
        javaTypeMap.put("varchar2", "String");
        javaTypeMap.put("tinytext", "String");
        javaTypeMap.put("text", "String");
        javaTypeMap.put("mediumtext", "String");
        javaTypeMap.put("longtext", "String");
        javaTypeMap.put("time", "Date");
        javaTypeMap.put("date", "Date");
        javaTypeMap.put("datetime", "Date");
        javaTypeMap.put("timestamp", "Date");
    }

    /**
     * 生成手脚架代码
     */
    public JfGenerator allRender(String table) {
        return javaRender(table);
    }

    /**
     * po 代码生成
     * */
    public JfGenerator modelRender(String tableName) {
        TableEntity tableDetail = getTableDetail(tableName);
        return model(tableDetail);
    }

    /**
     * po 代码生成
     * */
    public JfGenerator mapperRender(String tableName) {
        TableEntity tableDetail = getTableDetail(tableName);
        return mapper(tableDetail).mapperJava(tableDetail);
    }
    
    /**
     * java 代码生成
     * */
    public JfGenerator javaRender(String tableName) {
        TableEntity tableDetail = getTableDetail(tableName);
        return controller(tableDetail)
                .apiController(tableDetail)
                .feginApi(tableDetail)
                .fallBack(tableDetail)
                .service(tableDetail)
                .serviceImpl(tableDetail)
                .mapper(tableDetail)
                .mapperJava(tableDetail)
                .queryParam(tableDetail)
                .form(tableDetail)
                .model(tableDetail);
    }
    
    private String toClassNameUpperCase(String className) {
        return new StringBuffer(className.substring(0, 1).toUpperCase()).append(className.substring(1)).toString();
    }
    
    private String toPackages(String className,String packageSelf) {
        return new StringBuffer(packageBase).append(".").append(packageSelf).toString();
    }

    public JfGenerator model(TableEntity tableDetail){
        String packages = toPackages(tableDetail.getClassname(),modelUrl);
        String classNameCapital = tableDetail.getClassName();

        jfEngine.render("/template/java/model.html",
                Kv.by("package", packages)
                        .set("packageBase",packageBase)
                        .set("author",author)
                        .set("datetime", DateUtil.today())
                        .set("comments",tableDetail.getComments())
                        .set("hasBigDecimal",hasBigDecimal)
                        .set("tableColumns",tableDetail.getColumns())
                        .set("className", classNameCapital)
                        .set("classNameSmall", tableDetail.getClassname())
                ,
                new StringBuilder()
                        .append(System.getProperty("user.dir"))
                        .append("/")
                        .append(packages.replace(".", "/"))
                        .append("/")
                        .append(classNameCapital)
                        .append(".java")
        );
        return this;
    }


    public JfGenerator form(TableEntity tableDetail){
        String packages = toPackages(tableDetail.getClassname(),formUrl);
        String classNameCapital = tableDetail.getClassName();

        jfEngine.render("/template/java/form.html",
                Kv.by("package", packages)
                        .set("packageBase",packageBase)
                        .set("author",author)
                        .set("datetime", DateUtil.today())
                        .set("comments",tableDetail.getComments())
                        .set("hasBigDecimal",hasBigDecimal)
                        .set("tableColumns",tableDetail.getColumns())
                        .set("className", classNameCapital)
                        .set("classNameSmall", tableDetail.getClassname())
                ,
                new StringBuilder()
                        .append(System.getProperty("user.dir"))
                        .append("/")
                        .append(packages.replace(".", "/"))
                        .append("/")
                        .append(classNameCapital)
                        .append("DTO")
                        .append(".java")
        );
        return this;
    }


    public JfGenerator queryParam(TableEntity tableDetail){
        String packages = toPackages(tableDetail.getClassname(),queryUrl);
        String classNameCapital = tableDetail.getClassName();

        jfEngine.render("/template/java/queryParam.html",
                Kv.by("package", packages)
                        .set("packageBase",packageBase)
                        .set("author",author)
                        .set("datetime", DateUtil.today())
                        .set("comments",tableDetail.getComments())
                        .set("hasBigDecimal",hasBigDecimal)
                        .set("tableColumns",tableDetail.getColumns())
                        .set("className", classNameCapital)
                        .set("classNameSmall", tableDetail.getClassname())
                ,
                new StringBuilder()
                        .append(System.getProperty("user.dir"))
                        .append("/")
                        .append(packages.replace(".", "/"))
                        .append("/")
                        .append(classNameCapital)
                        .append("QueryParam")
                        .append(".java")
        );
        return this;
    }

    public JfGenerator mapper(TableEntity tableDetail){
        String packages = toPackages(tableDetail.getTableName(),mapperUrl);
        String classNameCapital = tableDetail.getClassName();

        jfEngine.render("/template/java/mapper.html",
                Kv.by("package", packageBase)
                        .set("author",author)
                        .set("datetime", DateUtil.today())
                        .set("comments",tableDetail.getComments())
                        .set("tableColumns",tableDetail.getColumns())
                        .set("pk",tableDetail.getPk())
                        .set("tableName",tableDetail.getTableName())
                        .set("className", classNameCapital)
                        .set("classname",tableDetail.getClassname())
                ,
                new StringBuilder()
                        .append(System.getProperty("user.dir"))
                        .append("/")
                        .append(packages.replace(".", "/"))
                        .append("/")
                        .append(classNameCapital)
                        .append("Mapper.xml")
        );
        return this;
    }

    public JfGenerator mapperJava(TableEntity tableDetail){
        String packages = toPackages(tableDetail.getTableName(),mapperUrl);
        String classNameCapital = tableDetail.getClassName();

        jfEngine.render("/template/java/mapperJava.html",
                Kv.by("package", packageBase)
                        .set("packageBase",packageBase)
                        .set("author",author)
                        .set("datetime", DateUtil.today())
                        .set("comments",tableDetail.getComments())
                        .set("tableColumns",tableDetail.getColumns())
                        .set("pk",tableDetail.getPk())
                        .set("tableName",tableDetail.getTableName())
                        .set("className", classNameCapital)
                        .set("classname",tableDetail.getClassname())
                ,
                new StringBuilder()
                        .append(System.getProperty("user.dir"))
                        .append("/")
                        .append(packages.replace(".", "/"))
                        .append("/")
                        .append(classNameCapital)
                        .append("Mapper.java")
        );
        return this;
    }


    /**
     * 生成Controller
     * @param tableDetail 表
     */
    public JfGenerator controller(TableEntity tableDetail){
        String controllerPackage = toPackages(tableDetail.getTableName(),controllerUrl);

        jfEngine.render("template/java/controller.html",
                Kv.by("package", controllerPackage)
                        .set("packageBase",packageBase)
                        .set("author",author)
                        .set("datetime", DateUtil.today())
                        .set("comments",tableDetail.getComments())
                        .set("tableName", tableDetail.getTableName())
                        .set("classname",tableDetail.getClassname())
                        .set("className", tableDetail.getClassName())
                        .set("pk",tableDetail.getPk())
                        ,
                new StringBuilder()
                        .append(System.getProperty("user.dir"))
                        .append("/")
                        .append(controllerPackage.replace(".", "/"))
                        .append("/")
                        .append(tableDetail.getClassName())
                        .append("Controller.java")
                );
        return this;
    }

    /**
     * 生成feginController
     * @param tableDetail 表
     */
    public JfGenerator apiController(TableEntity tableDetail){
        String controllerPackage = toPackages(tableDetail.getTableName(), controllerUrl);

        jfEngine.render("template/java/apiController.html",
                Kv.by("package", controllerPackage)
                        .set("packageBase",packageBase)
                        .set("author",author)
                        .set("datetime", DateUtil.today())
                        .set("comments",tableDetail.getComments())
                        .set("tableName", tableDetail.getTableName())
                        .set("classname",tableDetail.getClassname())
                        .set("className", tableDetail.getClassName())
                        .set("pk",tableDetail.getPk())
                ,
                new StringBuilder()
                        .append(System.getProperty("user.dir"))
                        .append("/")
                        .append(controllerPackage.replace(".", "/"))
                        .append("/")
                        .append(tableDetail.getClassName())
                        .append("ApiController.java")
        );
        return this;
    }


    /**
     * 生成feginApi
     * @param tableDetail 表
     */
    public JfGenerator feginApi(TableEntity tableDetail){
        String controllerPackage = toPackages(tableDetail.getTableName(),feginApiUrl);

        jfEngine.render("template/java/feginApi.html",
                Kv.by("package", controllerPackage)
                        .set("packageBase",packageBase)
                        .set("author",author)
                        .set("datetime", DateUtil.today())
                        .set("comments",tableDetail.getComments())
                        .set("tableName", tableDetail.getTableName())
                        .set("classname",tableDetail.getClassname())
                        .set("className", tableDetail.getClassName())
                        .set("pk",tableDetail.getPk())
                ,
                new StringBuilder()
                        .append(System.getProperty("user.dir"))
                        .append("/")
                        .append(controllerPackage.replace(".", "/"))
                        .append("/")
                        .append(tableDetail.getClassName())
                        .append("ApiService.java")
        );
        return this;
    }


    /**
     * 生成feginApiFallBack
     * @param tableDetail 表
     */
    public JfGenerator fallBack(TableEntity tableDetail){
        String controllerPackage = toPackages(tableDetail.getTableName(),feginApiFallbackUrl);

        jfEngine.render("template/java/apiFallBack.html",
                Kv.by("package", controllerPackage)
                        .set("packageBase",packageBase)
                        .set("author",author)
                        .set("datetime", DateUtil.today())
                        .set("comments",tableDetail.getComments())
                        .set("tableName", tableDetail.getTableName())
                        .set("classname",tableDetail.getClassname())
                        .set("className", tableDetail.getClassName())
                        .set("pk",tableDetail.getPk())
                ,
                new StringBuilder()
                        .append(System.getProperty("user.dir"))
                        .append("/")
                        .append(controllerPackage.replace(".", "/"))
                        .append("/")
                        .append(tableDetail.getClassName())
                        .append("ApiFallBack.java")
        );
        return this;
    }

    
    /**
     * 生成Service
     * @param tableDetail  表
     */
    public JfGenerator service(TableEntity tableDetail){
        String packages = toPackages(tableDetail.getTableName(),serviceUrl);

        String classNameCapital = tableDetail.getClassName();
        
        jfEngine.render("/template/java/service.html",
                Kv.by("package", packages)
                        .set("packageBase",packageBase)
                        .set("author",author)
                        .set("datetime", DateUtil.today())
                        .set("comments",tableDetail.getComments())
                        .set("pk",tableDetail.getPk())
                        .set("classname", tableDetail.getClassname())
                        .set("className",tableDetail.getClassName())
                        .set("tableName", tableDetail.getTableName())
                , 
                new StringBuilder()
                        .append(System.getProperty("user.dir"))
                        .append("/")
                        .append(packages.replace(".", "/"))
                        .append("/")
                        .append(tableDetail.getClassName())
                .append("Service.java")
                );
        return this;
    }

    /**
     * 生成Service
     * @param tableDetail  表
     */
    public JfGenerator serviceImpl(TableEntity tableDetail){
        String packages = toPackages(tableDetail.getTableName(),serviceUrl);

        String classNameCapital = tableDetail.getClassName();

        jfEngine.render("/template/java/serviceImpl.html",
                Kv.by("package", packages)
                        .set("packageBase",packageBase)
                        .set("author",author)
                        .set("datetime", DateUtil.today())
                        .set("comments",tableDetail.getComments())
                        .set("pk",tableDetail.getPk())
                        .set("classname", tableDetail.getClassname())
                        .set("className",tableDetail.getClassName())
                        .set("tableName", tableDetail.getTableName())
                ,
                new StringBuilder()
                        .append(System.getProperty("user.dir"))
                        .append("/")
                        .append(packages.replace(".", "/"))
                        .append("/impl/")
                        .append(tableDetail.getClassName())
                        .append("ServiceImpl.java")
        );
        return this;
    }
    
    /**
     * 生成html
     * @param tableName  类名称
     * */
    public JfGenerator htmlRender(String tableName){
        TableEntity tableDetail = getTableDetail(tableName);
        return htmlList(tableDetail);
    }
    
    
    public JfGenerator htmlList(TableEntity tableDetail){
        String packages = toPackages(tableDetail.getClassname(),"");
        String classNameCapital = tableDetail.getClassName();

        
        jfEngine.render("/template/html/list.html",
                Kv.by("tableColums", tableDetail.getColumns())
                .set("package", packages)
                .set("className", classNameCapital)
                .set("classname", tableDetail.getClassname())
                ,
                new StringBuilder()
                .append(System.getProperty("user.dir"))
                .append("/")
                .append(classNameCapital)
                .append("/")
                .append(tableDetail.getClassname())
                .append("List.html")
                );
        return this;
    }


    /**
     * 表名转换成Java类名
     */
    public static String tableToJava(String tableName, String tablePrefix) {
        if(StrUtil.isNotBlank(tablePrefix)){
            tableName = tableName.replace(tablePrefix, "");
        }
        return StrUtil.toCamelCase(tableName);
    }



    /**
     * 根据表名查询所有列信息
     * @param tableName
     * @return
     */
    private TableEntity getTableDetail(String tableName){
        _JFinalGenerator.initActiveRecordPlugin();
        String tableSql = "select table_name, engine, table_comment, create_time from information_schema.tables \n" +
                " where table_schema = (select database()) and table_name = ? limit 1 ";
        Record tableRecord = Db.findFirst(tableSql,tableName);
        String sql = "select column_name, data_type,column_key, column_comment, extra from information_schema.columns\n" +
                " where table_name = ? and table_schema = (select database()) order by ordinal_position";
        List<Record> records = Db.find(sql,tableName);
        List<ColumnEntity> columnEntities = new ArrayList<ColumnEntity>(records.size());

        TableEntity tableEntity = new TableEntity();

        ColumnEntity columnEntity = null;
        for (Record record : records) {
            columnEntity = new ColumnEntity();
            columnEntity.setAttrName(StrUtil.toCamelCase(record.getStr("column_name").toLowerCase()));
            columnEntity.setAttrname(StrUtil.toCamelCase(record.getStr("column_name").toLowerCase()));
            columnEntity.setAttrType(javaTypeMap.get(record.getStr("data_type")));
            columnEntity.setColumnName(record.getStr("column_name"));
            columnEntity.setComments(record.getStr("column_comment"));
            columnEntity.setDataType(record.getStr("data_type"));
            columnEntity.setExtra(record.getStr("extra"));

            //是否主键
            if("PRI".equalsIgnoreCase(record.getStr("column_key")) && tableEntity.getPk() == null){
                tableEntity.setPk(columnEntity);
            }

            if (!hasBigDecimal && "BigDecimal".equals(columnEntity.getAttrType())) {
                hasBigDecimal = true;
            }

            columnEntities.add(columnEntity);

        }
        String className = tableToJava(tableName,prefixes);
        tableEntity.setComments(tableRecord.getStr("table_comment"));
        tableEntity.setTableName(tableName);
        tableEntity.setClassName(toClassNameUpperCase(className));
        tableEntity.setClassname(className);
        tableEntity.setColumns(columnEntities);
        return tableEntity;
    }



}


